﻿//==============================================================================
// File: SettingsService.cs
// Created: 2010-05-28
// Author: Piotr Włodek, Arkadiusz Świerczek
//==============================================================================
// This file is a part of MAMMOTH project.
// Copyright (C) 2010 AGH University of Science and Technology, Krakow.
// https://caribou.iisg.agh.edu.pl/trac/mammoth
//
// Mammoth is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// Mammoth is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
// 
// You should have received a copy of the GNU Lesser General Public License
// along with Mammoth. If not, see http://www.gnu.org/licenses/.
//==============================================================================

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using WpfRcp.Core.Globals;
using WpfRcp.Core.Services;

namespace WpfRcp.Services
{
    public class SettingsService : ISettingsService
    {
        private const string MapKey = "SettingsService.ViewSettingsMap";
        private readonly IContainerFacade m_Container;
        private readonly IContainerFacade m_ChildContainer;
        
        public SettingsService(IContainerFacade container)
        {
            m_Container = container;
            m_Container.RegisterInstance<IApplicationSettings>(ApplicationSettings.Default);
            m_ChildContainer = m_Container.CreateChildContainer();
        }

        public IApplicationSettings Settings
        {
            get { return ApplicationSettings.Default; }
        }

        public void Save()
        {
            ApplicationSettings.Default.Save();
        }

        private static IDictionary<Type, IDictionary<Type, object>> ViewSettingsMap
        {
            get
            {
                IDictionary<Type, IDictionary<Type, object>> settingsMap;

                if (ApplicationSettings.Default.Generic.ContainsKey(MapKey) == false)
                {
                    settingsMap = new Dictionary<Type, IDictionary<Type, object>>();
                    ApplicationSettings.Default.Generic[MapKey] = settingsMap;

                    return settingsMap;
                }

                settingsMap = (IDictionary<Type, IDictionary<Type, object>>)ApplicationSettings.Default.Generic[MapKey];

                return settingsMap;
            }
        }

        public TSettings GetViewSettings<TView, TSettings>()
            where TView : class
            where TSettings : class 
        {
            return GetViewSettings(typeof(TView), typeof(TSettings)) as TSettings;
        }

        public TSettings GetViewSettings<TSettings>(Type viewType)
            where TSettings : class
        {
            return GetViewSettings(viewType, typeof (TSettings)) as TSettings;
        }

        private object GetViewSettings(Type viewType, Type settingsType)
        {
            object settings;
            IDictionary<Type, object> perViewSettings;

            if (ViewSettingsMap.ContainsKey(viewType))
            {
                perViewSettings = ViewSettingsMap[viewType];
                if (perViewSettings.ContainsKey(settingsType))
                    settings = perViewSettings[settingsType];
                else
                {
                    settings = m_ChildContainer.Resolve(settingsType);
                    Debug.Assert(settings != null);
                    perViewSettings.Add(settingsType, settings);
                }

                return settings;
            }

            settings = m_ChildContainer.Resolve(settingsType);
            Debug.Assert(settings != null);

            perViewSettings = new Dictionary<Type, object> { { settingsType, settings } };
            ViewSettingsMap.Add(viewType, perViewSettings);

            return settings;
        }

        public T GetGenericValue<T>(string key)
        {
            if (ApplicationSettings.Default.Generic.ContainsKey(key))
                return (T)ApplicationSettings.Default.Generic[key];

            return default(T);
        }

        public void SetGenericValue<T>(string key, T value)
        {
            if (ApplicationSettings.Default.Generic.ContainsKey(key))
                ApplicationSettings.Default.Generic.Remove(key);

            ApplicationSettings.Default.Generic.Add(key, value);
        }

        /// <summary>
        /// Registers custom settings.
        /// </summary>
        /// <typeparam name="T">Type of the settings class.</typeparam>
        /// <returns>Settings class singleton instance.</returns>
        public T RegisterSettingsClass<T>()
        {
            T options;
            var name = typeof (T).Name;

            if (ApplicationSettings.Default.Generic.ContainsKey(name))
            {
                options = (T)ApplicationSettings.Default.Generic[name];
                
                m_Container.BuildUp(options);
                m_Container.RegisterInstance(options);
            }
            else
            {
                m_Container.Register<T, T>(LifetimePolicy.Singleton);
                options = m_Container.Resolve<T>();

                ApplicationSettings.Default.Generic.Add(name, options);
            }

            return options;
        }

        /// <summary>
        /// Represents application wide settings that are serialized at the application shutdown and deserialized
        /// at the application startup.
        /// </summary>
        private sealed class ApplicationSettings : ApplicationSettingsBase, IApplicationSettings
        {
            private static readonly ApplicationSettings DefaultInstance;

            static  ApplicationSettings()
            {
                DefaultInstance = ((ApplicationSettings)(Synchronized(new ApplicationSettings())));
            }

            public ApplicationSettings()
            {
                if (Generic == null)
                {
                    Generic = new Dictionary<string, object>();
                }
                if (CurrentCulture == null)
                {
                    CurrentCulture = new CultureInfo("pl-PL");
                }
            }

            public static ApplicationSettings Default
            {
                get { return DefaultInstance; }
            }

            public void RestoreDefaults()
            {
                WorkspaceScale = 1.0;
                IsMaximized = true;
                IsFullScreen = false;
                IsHeaderVisible = false;
                NoSplash = false;
                CurrentTheme = ApplicationConstants.Themes.Dark;
                CurrentCulture = new CultureInfo("pl-PL");
                UseThreadPool = true;
                ThreadPoolSize = 3;

                if (DefaultsRestored != null)
                    DefaultsRestored(this, EventArgs.Empty);
            }

            public event EventHandler DefaultsRestored;

            [UserScopedSetting]
            [DefaultSettingValue("1.0")]
            public double WorkspaceScale
            {
                get { return (double)this["WorkspaceScale"]; }
                set { this["WorkspaceScale"] = value; }
            }

            [UserScopedSetting]
            [DefaultSettingValue("0.0")]
            public double WindowTop
            {
                get { return (double)this["WindowTop"]; }
                set { this["WindowTop"] = value; }
            }

            [UserScopedSetting]
            [DefaultSettingValue("0.0")]
            public double WindowLeft
            {
                get { return (double)this["WindowLeft"]; }
                set { this["WindowLeft"] = value; }
            }

            [UserScopedSetting]
            [DefaultSettingValue("700.0")]
            public double WindowWidth
            {
                get { return (double)this["WindowWidth"]; }
                set { this["WindowWidth"] = value; }
            }

            [UserScopedSetting]
            [DefaultSettingValue("400.0")]
            public double WindowHeight
            {
                get { return (double)this["WindowHeight"]; }
                set { this["WindowHeight"] = value; }
            }

            [UserScopedSetting]
            [DefaultSettingValue("true")]
            public bool IsMaximized
            {
                get { return (bool)this["IsMaximized"]; }
                set { this["IsMaximized"] = value; }
            }

            [UserScopedSetting]
            [DefaultSettingValue("false")]
            public bool IsFullScreen
            {
                get { return (bool)this["IsFullScreen"]; }
                set { this["IsFullScreen"] = value; }
            }

            [UserScopedSetting]
            [DefaultSettingValue("false")]
            public bool IsHeaderVisible
            {
                get { return (bool)this["IsHeaderVisible"]; }
                set { this["IsHeaderVisible"] = value; }
            }

            [UserScopedSetting]
            [DefaultSettingValue("false")]
            public bool NoSplash
            {
                get { return (bool)this["NoSplash"]; }
                set { this["NoSplash"] = value; }
            }

            [UserScopedSetting]
            public CultureInfo CurrentCulture
            {
                get { return (CultureInfo)this["CurrentCulture"]; }
                set
                {
                    if (value != null && !value.Equals(this["CurrentCulture"]))
                    {
                        this["CurrentCulture"] = value;
                    }
                }
            }

            [UserScopedSetting]
            [DefaultSettingValue(ApplicationConstants.Themes.Dark)]
            public string CurrentTheme
            {
                get { return (string)this["CurrentTheme"]; }
                set
                {
                    if (value != null && !value.Equals(this["CurrentTheme"]))
                    {
                        this["CurrentTheme"] = value;
                    }
                }
            }

            [UserScopedSetting]
            [SettingsSerializeAs(SettingsSerializeAs.Binary)]
            public IDictionary<string, object> Generic
            {
                get { return (IDictionary<string, object>)this["Generic"]; }
                set
                {
                    if (value != null)
                    {
                        this["Generic"] = value;
                    }
                }
            }

            [UserScopedSetting]
            [DefaultSettingValue("true")]
            public bool UseThreadPool
            {
                get { return (bool)this["UseThreadPool"]; }
                set { this["UseThreadPool"] = value; }
            }

            [UserScopedSetting]
            [DefaultSettingValue("3")]
            public int ThreadPoolSize
            {
                get { return (int)this["ThreadPoolSize"]; }
                set { this["ThreadPoolSize"] = value; }
            }
        }
    }
}